import { Color, NotEqualStencilFunc, ReplaceStencilOp, Vector2 } from "three";
import { Line2 } from "three/examples/jsm/lines/Line2";
import { LineGeometry } from "three/examples/jsm/lines/LineGeometry";
import { LineMaterial } from "three/examples/jsm/lines/LineMaterial";
import { App } from "../../application";
import { Point } from "../../Docs";

/**
 * 线条组件，功能和Polyline相似，主要区别在于频繁添加顶点的时候使用该组件的性能会比较高效
 */
export default class Polyline2 extends Line2 {
    private app: App;
    private pointsCount = 0; // 当前顶点数
    private segmentCount = 0; // 当前线段数
    private pointArray: Array<Point>;
    private colorArray: Array<Color>;
    private lineParam: { maxPoints?: number, pointArray?: Array<Point>, color?: string, lineWidth?: number, depthTest?: boolean, transparent?: boolean, opacity?: number, stencilRef?: number };
    private pointColor; // 使用lineParam中的颜色，默认为白色

    public constructor(lineParam: { maxPoints?: number, pointArray?: Array<Point>, color?: string, lineWidth?: number, depthTest?: boolean, transparent?: boolean, opacity?: number, stencilRef?: number }) {
        super();
        if (!lineParam) {
            lineParam = {};
        }

        this.lineParam = lineParam;
        if (!this.lineParam.maxPoints) {
            this.lineParam.maxPoints = 100;
        }
        this.pointColor = new Color(this.lineParam.color || '#ffffff'); // 使用lineParam中的颜色，默认为白色
        this.app = App.getInstance();

        this.pointArray = new Array<Point>();
        this.colorArray = new Array<Color>();

        this.buildGeometry();

        this.material = new LineMaterial({
            vertexColors: true, // 启用顶点颜色
            linewidth: this.lineParam.lineWidth || 5,
            resolution: new Vector2(window.innerWidth, window.innerHeight),
            depthTest: this.lineParam.depthTest !== undefined ? this.lineParam.depthTest : false,
            transparent: this.lineParam.transparent || false,
            opacity: this.lineParam.opacity || 1,
        });
        if (this.lineParam.stencilRef != undefined) {
            //设置模版缓冲区，用this.param.stencilRef覆盖模版缓冲区
            this.material.stencilWrite = true;
            this.material.stencilRef = this.lineParam.stencilRef;
            this.material.stencilFuncMask = 0xff;
            this.material.stencilFunc = NotEqualStencilFunc; //AlwaysStencilFunc;
            this.material.stencilFail = ReplaceStencilOp;
            this.material.stencilZFail = ReplaceStencilOp;
            this.material.stencilZPass = ReplaceStencilOp;
            this.material.needsUpdate = true;
        }

        // 如果pointArray有数据则添加初始顶点
        if (this.lineParam.pointArray && this.lineParam.pointArray.length > 0) {
            this.lineParam.pointArray.forEach(point => {
                this.addPoint(point);
            });
        }

        this.app.add(this);
    }

    private buildGeometry() {
        if (this.geometry) {
            this.geometry.dispose(); // 释放旧几何体GPU内存
        }
        this.geometry = new LineGeometry();
        // 预分配缓冲区：每条线段需6个位置值 + 6个颜色值
        this.geometry.setPositions(new Float32Array((this.lineParam.maxPoints - 1) * 6)); // 位置缓冲区
        this.geometry.setColors(new Float32Array((this.lineParam.maxPoints - 1) * 6));   // 颜色缓冲区 [1](@ref)
    }

    public addPoint(point: Point) {

        // 容量检查：超限时触发扩容
        if (this.pointsCount >= this.lineParam.maxPoints) {
            // this.resizeGeometry(this.maxPoints * 2); // 容量倍增策略
            this.pointsCount = 0;
            this.segmentCount = 0;
            this.lineParam.maxPoints = this.lineParam.maxPoints * 2;
            this.buildGeometry();

            let newPointArray = Array.from(this.pointArray);
            let newColorArray = Array.from(this.colorArray);
            this.pointArray.length = 0;
            this.colorArray.length = 0;

            let pointArrayLength = newPointArray.length;
            for (let i = 0; i < pointArrayLength; i++) {
                const pos = newPointArray[i].toEPSGWeb();
                this.addPoint(pos);
            }
        }

        this.pointArray.push(point.clone().toEPSGWeb());
        this.colorArray.push(this.pointColor.clone());

        point = point.clone().toEPSGWeb();
        const positions = this.geometry.attributes.instanceStart.array;
        const colors = this.geometry.attributes.instanceColorStart.array;

        // 添加第一个顶点（初始化第一条线段的起点）
        if (this.pointsCount === 0) {
            this.position.copy(point);
            // 位置
            // positions[0] = point.x; positions[1] = point.y; positions[2] = point.z;
            positions[0] = 0; positions[1] = 0; positions[2] = 0;
            // 颜色（范围[0,1]）
            colors[0] = this.pointColor.r; colors[1] = this.pointColor.g; colors[2] = this.pointColor.b;

            this.geometry.attributes.instanceStart.needsUpdate = true;
            this.geometry.attributes.instanceColorStart.needsUpdate = true;
        }
        // 添加后续顶点
        else {
            const segIndex = this.segmentCount * 6; // 当前线段在缓冲区的起始索引

            point.sub(this.position);

            // if(isNaN(point.x) || isNaN(point.y) || isNaN(point.z)){
            //     debugger
            // }

            // 更新上一条线段的终点位置和颜色
            positions[segIndex + 3] = point.x;
            positions[segIndex + 4] = point.y;
            positions[segIndex + 5] = point.z;
            colors[segIndex + 3] = this.pointColor.r;
            colors[segIndex + 4] = this.pointColor.g;
            colors[segIndex + 5] = this.pointColor.b;

            // 初始化新线段的起点（位置与终点重合，颜色相同）
            positions[segIndex + 6] = point.x;
            positions[segIndex + 7] = point.y;
            positions[segIndex + 8] = point.z;
            colors[segIndex + 6] = this.pointColor.r;
            colors[segIndex + 7] = this.pointColor.g;
            colors[segIndex + 8] = this.pointColor.b;

            this.segmentCount++; // 线段数量+1
            this.geometry.instanceCount = this.segmentCount; // 更新渲染的线段数 [1](@ref)

            // 标记所有相关属性需更新
            this.geometry.attributes.instanceStart.needsUpdate = true;
            this.geometry.attributes.instanceEnd.needsUpdate = true;
            this.geometry.attributes.instanceColorStart.needsUpdate = true;
            this.geometry.attributes.instanceColorEnd.needsUpdate = true;
        }
        // this.geometry.computeBoundingBox();
        this.geometry.computeBoundingBox();
        this.geometry.computeBoundingSphere();
        this.computeLineDistances();
        this.pointsCount++;
    }

    /**
     * 删除线中的一个点
     * @param index 要删除点的索引。这个索引对应数组中的下标，索引值为0对应数组中的第一个元素 
     * @example polyline.removePoint(0);
     */
    /**
     * 删除线中的一个点
     * @param index 要删除点的索引。这个索引对应数组中的下标，索引值为0对应数组中的第一个元素 
     * @example polyline.removePoint(0);
     */
    public removePoint(index: number) {
        // 检查索引是否有效
        if (index < 0 || index >= this.pointsCount) {
            console.warn('索引超出范围');
            return;
        }

        // 保存剩余的点和颜色
        const remainingPoints = [];
        const remainingColors = [];

        for (let i = 0; i < this.pointsCount; i++) {
            if (i !== index) {
                remainingPoints.push(this.pointArray[i]);
                remainingColors.push(this.colorArray[i]);
            }
        }

        // 重置并重新构建几何体
        this.pointsCount = 0;
        this.segmentCount = 0;
        this.buildGeometry();
        this.pointArray.length = 0;
        this.colorArray.length = 0;

        // 重新添加剩余的点
        for (let i = 0; i < remainingPoints.length; i++) {
            this.addPoint(remainingPoints[i]);
        }
    }

    /**
     * 往线条中适当的位置添加一个点
     * param index 要插入的索引位置
     * @param newPoint 新的点
     */
    /**
     * 往线条中适当的位置添加一个点
     * @param index 要插入的索引位置
     * @param newPoint 新的点
     * @param newColor 新点的颜色
     */
    public insertPoint(index: number, newPoint: Point, newColor: Color) {
        // 检查索引是否有效
        if (index < 0 || index > this.pointsCount) {
            console.warn('索引超出范围');
            return;
        }

        // 在数组中插入新的点和颜色
        this.pointArray.splice(index, 0, newPoint.clone().toEPSGWeb());
        this.colorArray.splice(index, 0, newColor.clone());

        // 重置并重新构建几何体
        this.pointsCount = 0;
        this.segmentCount = 0;
        this.buildGeometry();

        // 重新添加所有的点
        for (let i = 0; i < this.pointArray.length; i++) {
            this.addPoint(this.pointArray[i]);
        }
    }

    public getPointArray() {
        return this.pointArray;
    }
}